using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using System.Data.SqlClient;
using System.Windows.Threading;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.ComponentModel;

namespace AlgoLogicMessengerCompetitor
{
    public delegate void NextMoveNotify(Move nextMove);

    public delegate void ThreadDelegate();

    /// <summary>
    /// Interaction logic for GameWindow.xaml
    /// </summary>
    public partial class GameWindow: Page
    {
        public int dimension = 1;

        private readonly GameLogic gameLogic = new GameLogic();
        private GameBoard gameGrid;
        private PlayerNumber currentPlayer = PlayerNumber.First;
        private PlayerNumber currentTurn = PlayerNumber.First;
        private readonly Dictionary<PlayerNumber, String> playerSymbol = new Dictionary<PlayerNumber, string>();
        private bool isOnlineMode;
        private readonly Communication onlineCommunication = new Communication();

        private GameType gameType = GameType.HumanVsHuman; //default to human vs human


        System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
        NetworkStream serverStream = default(NetworkStream);
        BackgroundWorker worker = new BackgroundWorker();
        Thread ctThread1;

        public GameWindow()
        {
            InitializeComponent();
            MakeGameBoard();
            playerSymbol[PlayerNumber.First] = "O";
            playerSymbol[PlayerNumber.Second] = "X";
            onlineCommunication.NotifyCommunication += CommunicationMessage;


            clientSocket.Connect(IP.Address, 8888);
            serverStream = clientSocket.GetStream();
            byte[] outStream = System.Text.Encoding.ASCII.GetBytes("" + "#");
            serverStream.Write(outStream, 0, outStream.Length);
            serverStream.Flush();
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                getCommand();
            };
            worker.RunWorkerAsync();
        }

        private void getCommand()
        {
            while (true)
            {
                try
                {
                    serverStream = clientSocket.GetStream();
                    int bufferSize = clientSocket.ReceiveBufferSize;
                    byte[] inStream = new byte[bufferSize];
                    serverStream.Read(inStream, 0, bufferSize);

                    string str = System.Text.Encoding.ASCII.GetString(inStream, 0, inStream.Length);
                    str = str.Replace("\0", "");
                    string[] array1 = str.Split('|');

                    if (str.StartsWith("@quizStart"))
                    {
                        //Thread.Sleep(100);
                        this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                        new Action(delegate()
                        {
                            NavigationFrame.Content = null;
                            NavigationFrame.NavigationService.RemoveBackEntry();
                            QuestionAnswer Qa = new QuestionAnswer();
                            NavigationFrame.Content = Qa;
                            //NavigationService nav = NavigationService.GetNavigationService(this);
                            //nav.Navigate(new System.Uri("QuestionAnswer.xaml", UriKind.RelativeOrAbsolute));
                        }
                          )
                          );
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }


        private void CommunicationMessage(CommunicationState state, string message)
        {
            if (state == CommunicationState.Connected)
            {
                isOnlineMode = true;
                Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadDelegate(MakeGameBoard));
                Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadDelegate(ConnectedMessage));
            }
            else if (state == CommunicationState.MessageRecieved)
            {
                if (message[0] == 't') //turn message
                {
                    int x = Convert.ToInt32(message[1].ToString());
                    int y = Convert.ToInt32(message[2].ToString());
                    int z = Convert.ToInt32(message[3].ToString());
                    int p = Convert.ToInt32(message[4].ToString());

                    Move recievedMove = new Move(x, y, z);
                    if (p == ((int) PlayerNumber.First))
                    {
                        recievedMove.Player = PlayerNumber.First;
                    }
                    else if (p == ((int) PlayerNumber.Second))
                    {
                        recievedMove.Player = PlayerNumber.Second;
                    }
                    else
                    {
                        return; //error
                    }
                    Dispatcher.Invoke(DispatcherPriority.Normal, new NextMoveNotify(ThreadSafeUpdate), recievedMove);
                }
                else if (message[0] == 'g') //new game message
                {
                    Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadDelegate(MakeGameBoard));
                }
            }
        }

        private void ConnectedMessage()
        {
          
        }

        private void ThreadSafeUpdate(Move recievedMove)
        {
            ProcessMove(recievedMove);
        }

        private void OnHumanVsHuman(object sender, RoutedEventArgs e)
        {
            gameType = GameType.HumanVsHuman;
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            currentPlayer = PlayerNumber.First;
            MakeGameBoard();
        }


        /// <summary>
        /// Called when a new offline game is being created
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnNewGame(object sender, RoutedEventArgs e)
        {
            currentPlayer = PlayerNumber.First;
            MakeGameBoard();
        }

        private void OnNextMove(Move recievedMove)
        {
            recievedMove.Player = currentPlayer;
            if (currentPlayer != currentTurn) return;

            if (isOnlineMode && !onlineCommunication.IsConnected)
            {
                return;
            }

            if (ProcessMove(recievedMove))
            {
                if (isOnlineMode)
                {
                    string message = "t" + recievedMove;
                    onlineCommunication.WriteMessage(message);
                }
            }
        }

        private bool ProcessMove(Move recievedMove)
        {
            MoveResult result = gameLogic.PlaceMove(recievedMove);
            if (result.status == MoveStatus.GameOver || result.status == MoveStatus.InvalidMove)
            {
                return false;
            }

            //this move is valid so place the peice on the board
            gameGrid.PlaceMoveInGrid(recievedMove, playerSymbol[recievedMove.Player]);

            if (result.status == MoveStatus.WinningMove)
            {
                gameInfo.Content = playerSymbol[recievedMove.Player] + " is the winner!";
                gameGrid.MarkWinningLine(result.moveList);
            }
            else if (result.status == MoveStatus.TurnOver)
            {
                ToggleCurrentPlayer();
                UpdateTurnInfo();
            }
            else if (result.status == MoveStatus.TieGame)
            {
                gameInfo.Content = "Draw";
                return true;
            }

            return false;
        }

        private void ToggleCurrentPlayer()
        {
            if (!isOnlineMode)
            {
                if (currentPlayer == PlayerNumber.First)
                {
                    currentPlayer = PlayerNumber.Second;
                }
                else
                {
                    currentPlayer = PlayerNumber.First;
                }
            }

            if (currentTurn == PlayerNumber.First)
            {
                currentTurn = PlayerNumber.Second;
            }
            else
            {
                currentTurn = PlayerNumber.First;
            }
        }

        private void UpdateTurnInfo()
        {
            if (!isOnlineMode)
            {
                if (currentPlayer == currentTurn)
                {
                    gameInfo.Content = "It is " + playerSymbol[currentPlayer] + "'s turn";
                }
                else
                {
                    gameInfo.Content = "Waiting for " + playerSymbol[currentTurn] + " to move";
                }
            }
        }

        private void MakeGameBoard()
        {
            int d = dimension + 2;
            gameLogic.Dimension = d; //Win

            if (!gameLogic.SetUpBoardLogic())
            {
                return;
            }

            currentTurn = PlayerNumber.First;
            if (gameGrid != null)
            {
                GridHostingPanel.Children.Remove(gameGrid);
            }

            gameGrid = new GameBoard();
            gameGrid.nextMoveNotify += OnNextMove;
            gameGrid.Loaded += gameGrid_Loaded;
            gameGrid.Dimension = dimension;
            gameGrid.BoardWidth = 600;

            gameGrid.SetupGameBoard();

            GridHostingPanel.Children.Add(gameGrid);
        }

        private void gameGrid_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateTurnInfo();
        }


        private void button4_Click(object sender, RoutedEventArgs e)
        {
            NavigationService nav = NavigationService.GetNavigationService(this);
            nav.Navigate(new System.Uri("NavigationPage.xaml", UriKind.RelativeOrAbsolute));
        }
    }
}